[PHP] DTreeとPHPで作ったWeb RSSリーダー | A Day In The Boy's Life

A Day In The Boy's Life

とあるエンジニアのとある1日のつぶやき。

以前に書いた、「[PHP]PEARパッケージXML_RSSとHTTP_Requestを使ったWebRSSリーダー 」と「JavaScriptで簡単にWindowsエクスプローラー風のツリーを作れるDTree 」を熱烈に合体させてみる。


できたのは、こんな感じ


DTreeRSS


結構いい感じなのではと自分なりに思ってみる。

DTreeの特長であるエクスプローラの機能も使えますし、フィードを自由に折りたたんだり、開いたりする事もできます。

流れとしては、PHPでRSSフィードを取得・解析し、フィードに含まれる記事を出力する際にDTreeの制御関数(JavaScript)を動的に作り出していると言った具合です。

サイトのタイトル部分をエクスプローラ風に折りたためたり、各記事のタイトルをクリックすると、その記事にリンクするようになっています。


ソースはちょっと長いですが・・・。

動作環境は、Linux(RedHat)+PHP4.3.11(+PEAR:XML_RSS、PEAR:HTTP_Request)+DTreeです。


一応、こんな感じのディレクトリ構成を想定しています。


/rss

  ┣ php ━ rss_reader.php (下記ソース)

  ┗ dtree ━ dtree.js

        ┣ dtree.css

        ┗ img


<?php

require_once "XML/RSS.php";
require_once "HTTP/Request.php";

// プロキシサーバー
$proxy = "hoge-proxy.hogehoge.com";
// プロキシ経由で取得したRSSフィード情報の一時保存ファイル
$tmp_file_base = "/tmp/";

// フィードの登録実行処理
if ($_REQUEST['operate'] == "regist") {

// 社外のRSSフィードを取得する場合
    if ($_REQUEST['network'] == "outside") {

// 取得したRSSフィードをローカルに保存する際のファイル名
        $feed_path = $tmp_file_base . uniqid("rss_") . ".xml";;

// HTTP_Requestを通して、RSSフィードを取得
        $req = &new HTTP_Request($_REQUEST['rss_feed']);
// インターネットに出る際に通るプロキシ
        $req->setProxy($proxy);
// リクエストを送信
        $req->sendRequest();

// 取得したRSSフィードの内容をファイルに書き出しておく
        $fp = fopen($feed_path, "w");
        if (fwrite($fp, $req->getResponseBody()) === FALSE) {
            echo $tmp_file . "の作成に失敗しました)";
            exit;
        }
    fclose($fp);
    } else {
// プロキシを通さない場合はそのままRSSフィードのURLをセット
        $feed_path = $_REQUEST['rss_feed'];
    }
}

echo "
<html>
<title>Web RSS Reader</title>
<head>
<link rel='StyleSheet' href='/rss/dtree/dtree.css' type='text/css' />
<script type='text/javascript' src='/rss/dtree/dtree.js'></script>
<meta http-equiv='Content-Type' content='text/html;charset=EUC-JP'>
</head>
<body>
<form action='{$_SERVER['PHP_SELF']}?operate=regist' method='post'>
<table bgcolor='#EFEFEF' width='600' cellpading='1' cellspacing='3' border='0'>
<tr>
<td width='220'><b>RSSフィード</b></td>
<td width='350'><input type='text' name='rss_feed' size='65' value='http://'></td>
<td width='30'><input type='submit' value='登録'></td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type='radio' name='network' value='outside' checked>社外
<input type='radio' name='network' value='inside'>社内</td>
<td>&nbsp;</td>
</tr>
</table>
</form>
<div class='dtree'>
<p><a href='javascript: d.openAll();'>open all</a> | <a href='javascript: d.closeAll();'>close all</a></p>
<script type='text/javascript'>
<!--

d = new dTree('d');
// DTreeにルートノードをセット
d.add(0,-1,'My RSS Feed');
";
// DTreeの管理ID
$dtree_id = 1;
// DTreeの親のID
$dtree_pid = 0;

// XML_RSSを使って、取得したRSSフィードを解析
$rss =& new XML_RSS($feed_path);
$rss->parse();

// サイトのタイトルを80文字で省略し、文字コードを内部文字エンコーディング(EUC-JP)に変更
$feed_title = mb_strimwidth(mb_convert_encoding($rss->channel['title'], mb_internal_encoding(), 'auto'), 0, 80, "...");

// DTreeにサイトのタイトルをセットする
echo "d.add($dtree_id, $dtree_pid, \"{$feed_title}\", \"{$rss->channel['link']}\", '', '_blank', '/rss/dtree/img/globe.gif', '/rss/dtree/img/globe.gif');";

$dtree_pid = $dtree_id;
$dtree_id++;
$item = array();

// RSSフィードの内容を解析して各記事を表示
foreach ($rss->getItems() as $item) {
// 内部用のRSSフィードの場合は、EUC-JPに変換
    $title = mb_strimwidth(mb_convert_encoding($item['title'], mb_internal_encoding(), 'auto'), 0, 80, "...");

// RSS1.0の場合
    if ($item['dc:date']) {
        preg_match("/([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})\+([0-9]{2}):([0-9]{2})/", $item['dc:date'], $matches);
        $dc_date = $matches[1] . "/" . $matches[2] . "/" . $matches[3] . " " . $matches[4] . ":" . $matches[5];
    } else {
// RSS2.0の場合
$dc_date = date("Y/m/d H:i", strtotime($item['pubdate'])); } // DTreeの第2階層にRSSフィードに含まれる各記事情報をセット
echo "d.add($dtree_id, $dtree_pid, '$title', '{$item['link']}', '最終更新日: $dc_date', '_blank');"; $dtree_id++; } // 一時ファイルを削除 if ($_REQUEST['network'] == "outside") { unlink($feed_path); } echo " document.write(d); //--> </script> </div></body></html> "; ?>

[PHP]PEARパッケージXML_RSSとHTTP_Requestを使ったWebRSSリーダー 」で書いたように、プロキシを経由してもRSSを取得できるようにしています。

プログラム内のRSSフィード取得方法についての解説は、上記の記事を参考にしてください。


あと、本プログラムには取得したRSSフィードを保存する機能を付けていません。

また、RSSフィード自体のエラーチェックもしていません。

参考程度に見てもらえればと思います。


主に解説する点としては、RSSフィードの情報をDTreeに渡す点でしょうか。


<script type='text/javascript'>
<!--
d = new dTree('d');
// DTreeにルートノードをセット
d.add(0,-1,'My RSS Feed');
";
$dtree_id = 1;
$dtree_pid = 0;

・・・・・・

// DTreeにサイトのタイトルをセットする
echo "d.add($dtree_id, $dtree_pid, \"{$feed_title}\", \"{$rss->channel['link']}\", '', '_blank', '/rss/dtree/img/globe.gif', '/rss/dtree/img/globe.gif');";

の部分で、DTreeのルートノードの作成と、サイトのタイトルを表示させています。

後は、フィード情報に含まれる記事情報の数だけループさせて、先ほどセットしたサイトのタイトルの階層にぶら下がる記事の階層をDTreeで作っています。


※ DTreeについては、「JavaScriptで簡単にWindowsエクスプローラー風のツリーを作れるDTree 」参照


foreach ($rss->getItems() as $item) {

・・・・・・

// DTreeの第2階層にRSSフィードに含まれる各記事情報をセット
echo "d.add($dtree_id, $dtree_pid, '$title', '{$item['link']}', '最終更新日: $dc_date', '_blank');";
$dtree_id++;
}


余談ですが、上記のループの部分で行っている


// RSS1.0の場合
if ($item['dc:date']) {
    preg_match("/([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})\+([0-9]{2}):([0-9]{2})/", $item['dc:date'], $matches);
    $dc_date = $matches[1] . "/" . $matches[2] . "/" . $matches[3] . " " . $matches[4] . ":" . $matches[5];
} else {
// RSS2.0の場合
$dc_date = date("Y/m/d H:i", strtotime($item['pubdate'])); }

の部分では、RSS1.0に含まれるフィードは、記事の更新情報が「dc:date」というタグに埋め込まれていますが、RSS2.0になると「pubdate」と言うタグに更新情報が含まれます。

RSS1.0の場合、更新情報は「2007-09-05T00:46:02+09:00」と言う形式で保存されていますが、RSS2.0になると「Wed, 05 Sep 2007 00:46:02 +0900」という形式になります。

RSS2.0の保存形式の場合、PHPのstrtotime関数でUnixタイムスタンプにそのまま変換する事が可能です。


最後に、DTreeのルートノードの画像を変更しています。

デフォルトだとWindowsのマイコンピュータのようなアイコンですので、RSSで良くあるアイコンに変更してます。

アイコンとかは、「Feedアイコン 」とかを探せばいいものが見つかるかも。

変更の仕方は、以前に書いた記事のようにdtree.jsファイルの中の画像パスを変更する事で可能です。